﻿#nullable enable
using System;
using Avalonia.Collections;
using Avalonia.Media;
using Avalonia.Media.Immutable;
using Xunit;

namespace Avalonia.Base.UnitTests.Media
{
    public class PenTests
    {
        [Fact]
        public void Changing_Thickness_Raises_Invalidated()
        {
            var target = new Pen();

            RenderResourceTestHelper.AssertResourceInvalidation(target, () =>
            {
                target.Thickness = 18;
            });
        }

        [Fact]
        public void Brush_Is_Added_To_The_Same_Compositor()
        {
            var brush = new SolidColorBrush(Colors.Red);
            var target = new Pen { Brush = brush };

            using var helper = new RenderResourceTestHelper();
            helper.AssertExistsOnCompositor(brush, false);
            helper.AddToCompositor(target);
            helper.AssertExistsOnCompositor(brush);
            Assert.True(helper.IsInvalidated(brush));
        }

        [Fact]
        public void Changing_DashStyle_Dashes_Raises_Invalidated()
        {
            var dashes = new DashStyle();
            var target = new Pen { DashStyle = dashes };

            RenderResourceTestHelper.AssertResourceInvalidation(target, () =>
            {
                dashes.Dashes = new AvaloniaList<double> { 0.1, 0.2 };
            });
        }

        [Fact]
        public void Adding_DashStyle_Dashes_Raises_Invalidated()
        {
            var dashes = new DashStyle();
            var target = new Pen { DashStyle = dashes };
            RenderResourceTestHelper.AssertResourceInvalidation(target, () =>
            {
                dashes.Dashes = new AvaloniaList<double>
                {
                    0.3
                };
            });
        }        
        
        [Fact]
        public void Adding_DashStyle_Dash_Raises_Invalidated()
        {
            var dashes = new DashStyle();
            var target = new Pen { DashStyle = dashes };
            dashes.Dashes = new AvaloniaList<double>
            {
                0.3
            };
            RenderResourceTestHelper.AssertResourceInvalidation(target, () =>
            {
                dashes.Dashes.AddRange(new[] { 1.0, 2 });
            });
        }

        [Fact]
        public void Equality_Is_Implemented_Between_Immutable_And_Mutable_Pens()
        {
            var brush = new ImmutableSolidColorBrush(Colors.Red);
            var target1 = new ImmutablePen(
                brush: brush,
                thickness: 2,
                dashStyle: (ImmutableDashStyle)DashStyle.Dash,
                lineCap: PenLineCap.Round,
                lineJoin: PenLineJoin.Round,
                miterLimit: 21);
            var target2 = new Pen(
                brush: brush,
                thickness: 2,
                dashStyle: DashStyle.Dash,
                lineCap: PenLineCap.Round,
                lineJoin: PenLineJoin.Round,
                miterLimit: 21);

            Assert.True(Equals(target1, target2));
        }

        [Fact]
        public void Equality_Is_Implemented_Between_Mutable_And_Immutable_DashStyles()
        {
            var brush = new ImmutableSolidColorBrush(Colors.Red);
            var target1 = new ImmutablePen(
                brush: brush,
                thickness: 2,
                dashStyle: new ImmutableDashStyle(new[] { 0.1, 0.2 }, 5),
                lineCap: PenLineCap.Round,
                lineJoin: PenLineJoin.Round,
                miterLimit: 21);
            var target2 = new Pen(
                brush: brush,
                thickness: 2,
                dashStyle: new DashStyle(new[] { 0.1, 0.2 }, 5),
                lineCap: PenLineCap.Round,
                lineJoin: PenLineJoin.Round,
                miterLimit: 21);

            Assert.True(Equals(target1, target2));
        }
        
        [Fact]
        public void TryModifyOrCreate_Should_Return_True_When_Previous_Exists_And_Assign_Null_When_Brush_Is_Null()
        {
            IPen? target = new ImmutablePen(
                brush: new ImmutableSolidColorBrush(Colors.Red),
                thickness: 2,
                dashStyle: new ImmutableDashStyle(new[] { 0.1, 0.2 }, 5),
                lineCap: PenLineCap.Round,
                lineJoin: PenLineJoin.Round,
                miterLimit: 21);

            var result = Pen.TryModifyOrCreate(ref target, null, 2);

            Assert.True(result);
            Assert.Null(target);
        }

        [Fact]
        public void TryModifyOrCreate_Should_Return_False_When_Previous_Not_Exists_And_Assign_Null_When_Brush_Is_Null()
        {
            IPen? target = null;

            var result = Pen.TryModifyOrCreate(ref target, null, 2);

            Assert.False(result);
            Assert.Null(target);
        }
        
        [Fact]
        public void TryModifyOrCreate_Should_Return_True_When_Previous_Immutable_And_Assign_Mutable_When_Brush_Is_Mutable()
        {
            IPen? target = new ImmutablePen(
                brush: new ImmutableSolidColorBrush(Colors.Red),
                thickness: 2,
                dashStyle: new ImmutableDashStyle(new[] { 0.1, 0.2 }, 5),
                lineCap: PenLineCap.Round,
                lineJoin: PenLineJoin.Round,
                miterLimit: 21);

            var result = Pen.TryModifyOrCreate(ref target, new SolidColorBrush(Colors.Blue), 2);

            Assert.True(result);
            Assert.IsType<Pen>(target);
        }
        
        [Fact]
        public void TryModifyOrCreate_Should_Return_True_When_Previous_Immutable_And_Assign_Immutable_When_Brush_Is_Immutable()
        {
            IPen? target = new ImmutablePen(
                brush: new ImmutableSolidColorBrush(Colors.Red),
                thickness: 2,
                dashStyle: new ImmutableDashStyle(new[] { 0.1, 0.2 }, 5),
                lineCap: PenLineCap.Round,
                lineJoin: PenLineJoin.Round,
                miterLimit: 21);

            var result = Pen.TryModifyOrCreate(ref target, new ImmutableSolidColorBrush(Colors.Blue), 2);

            Assert.True(result);
            Assert.IsType<ImmutablePen>(target);
        }
        
        [Fact]
        public void TryModifyOrCreate_Should_Return_False_When_Previous_Mutable_And_Modify_Mutable_When_Brush_Is_Mutable()
        {
            var oldPen = new Pen(
                brush: new SolidColorBrush(Colors.Red),
                thickness: 2,
                dashStyle: new ImmutableDashStyle(new[] { 0.1, 0.2 }, 5),
                lineCap: PenLineCap.Round,
                lineJoin: PenLineJoin.Round,
                miterLimit: 21);
            IPen? target = oldPen;

            var result = Pen.TryModifyOrCreate(ref target, new SolidColorBrush(Colors.Blue), 2);

            Assert.False(result);
            Assert.Same(oldPen, target);
        }
    }
}
